package org.cllib.tool;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.cllib.math.Math;

public class Util {
	private final static byte[] tableOfOnes ;
	static{
		tableOfOnes = new byte[256];
		for(int i = 0 ;i <= 255 ;i ++){
			tableOfOnes[i] = (byte)getNumberOfOnes(i);
		}
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for(int i = Integer.MIN_VALUE ; ; i ++){
			int sc = getNumberOfOnesByDefinition(i);
			
			int c1 = getNumberOfOnes(i);
			
			int c2 = getNumberOfOnes2(i);
			
			int c3 = getNumberOfOnes3(i);
			
			if(sc != c1 || sc != c2 || sc != c3){
				throw new IllegalStateException("sc = " + sc + " c1 = " + c1 + " c2 = " + c2 + " c3 = " + c3);
			}
			
			if(i == Integer.MAX_VALUE){
				break;
			}
		}
		
		System.out.println("ok");
	}

	public static int[] unpack(List<Integer> l) {
		int[] a = new int[l.size()];
		for (int i = 0; i < a.length; i++) {
			a[i] = l.get(i);
		}
		return a;
	}

	public static int[] getDigits(int n) {
		List<Integer> l = new ArrayList<Integer>();
		do {
			l.add(n % 10);
			n /= 10;
		} while (n > 0);
		return Util.unpack(l);
	}

	public static int getNumberOfDecimalDigits(int n) {
		return getNumberOfDigits(n, 10);
	}

	public static int getNumberOfDigits(int n, int radix) {
		int count = 0;
		do {
			count++;
			n /= radix;
		} while (n > 0);
		return count;
	}

	public static int getSumOfDigits(int n) {
		n = Math.abs(n);

		int[] digits = getDigits(n);
		int sum = 0;
		for (int i = 0; i < digits.length; i++) {
			sum += digits[i];
		}

		return sum;
	}

	public static void rm(File file) {
		if (file == null) {
			System.out.println("input file null");
		}

		if (!file.exists()) {
			System.out.println("file to delete does not exist["
					+ file.getAbsolutePath() + "]");
		}

		if (file.isDirectory()) {
			File[] files = file.listFiles();
			if (files != null && files.length > 0) {
				for (File f : files) {
					rm(f);
				}
			}
		}

		boolean deleted = file.delete();
		if (!deleted) {
			System.out.println("unable to delete [" + file.getAbsolutePath()
					+ "]");
		}
	}

	public static byte[] getBytesFromFile(File file) throws Exception {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		byte[] b = new byte[10 * 1024];

		FileInputStream fis = new FileInputStream(file);
		int length = -1;
		while ((length = fis.read(b, 0, b.length)) != -1) {
			baos.write(b, 0, length);
		}
		fis.close();
		return baos.toByteArray();
	}

	public static void putBytesToFile(byte[] data, File file) throws Exception {
		FileOutputStream fos = new FileOutputStream(file);

		fos.write(data, 0, data.length);

		fos.flush();
		fos.close();
	}
	
	public static int getNumberOfOnes(int n){
		int c = 0;
		while(n != 0){
			if((n & 1) == 1){
				c ++;
			}
			
			n >>>= 1;
		}
		
		return c;
	}
	
	public static int getNumberOfOnes2(int n){
		return tableOfOnes[(n & 0xff000000) >>> 24] + tableOfOnes[(n & 0xff0000) >>> 16] + tableOfOnes[(n & 0xff00) >>> 8] + tableOfOnes[n & 0xff];
	}
	
	public static int getNumberOfOnes3(int n){
		n = ((n & 0xaaaaaaaa) >>> 1) + (n & 0x55555555);
		n = ((n & 0xcccccccc) >>> 2) + (n & 0x33333333);
		n = ((n & 0xf0f0f0f0) >>> 4) + (n & 0x0f0f0f0f);
		n = ((n & 0xff00ff00) >>> 8) + (n & 0x00ff00ff);
		n = ((n & 0xffff0000) >>> 16) + (n & 0x0000ffff);
		return n;
	}
	
	private static int getNumberOfOnesByDefinition(int n){
		int c = 0;
		
		for(int i = 0 ;i < 32 ;i ++){
			if((n & 1) != 0){
				c ++;
			}
			n >>>= 1;
		}
		
		return c;
	}
	
	public static File getSharedPath(File[] fs){
		if(fs == null || fs.length <= 0){
			throw new IllegalArgumentException("no input specified");
		}
		
		for(int i = 0 ;i < fs.length ;i ++){
			if(fs[i] == null){
				throw new IllegalArgumentException("[" + i + "] null");
			}
			
			if(!fs[i].exists()){
				throw new IllegalArgumentException("[" + i + "] does not exist");
			}
		}
		
		Map<Integer, List<File>> fgs = new HashMap<Integer, List<File>>();
		for(int i = 0 ;i < fs.length ;i ++){
			File _f = new File(fs[i].getAbsolutePath());
			
			Stack<File> stack = new Stack<File>();
			
			while(_f != null){
				stack.push(_f);
				_f = _f.getParentFile();
			}
			
			List<File> _fs = new ArrayList<File>();
			
			while(!stack.isEmpty()){
				_fs.add(stack.pop());
			}
			
			fgs.put(i, _fs);
		}
		
		File result = null;
		outer :for(int i = 0 ; ;i ++){
			for(int j = 0 ;j < fgs.size() ;j ++){
				if(i >= fgs.get(j).size()){
					break outer;
				}
			}
			
			File _result = fgs.get(0).get(i);
			for(int j = 1 ;j < fgs.size(); j ++){
				try {
					String _s1 = _result.getCanonicalPath();
					String _s2 = fgs.get(j).get(i).getCanonicalPath();
					
					if(_s1.equals(_s2)){
						
					}else{
						_result = null;
						break outer;
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			result = _result;			
		}
		return result;
	}
	
	/**
	 * how to reach f2 starting from f1
	 * which means f2 = f1.getAbsulutePath + getRelativePath(f1 , f2)
	 * 
	 * @param f1
	 * @param f2
	 * 
	 * @return null if impossible to perform the movement
	 */
	public static String getRelativePath(File f1 , File f2){
		if(f1 == null || !f1.exists() || f2 == null || !f2.exists()){
			throw new IllegalStateException("input illegal [f1 = " + f1 + " f2 = " + f2 + "]");
		}
		
		String s1 = null;
		try {
			s1 = f1.getCanonicalPath();
		} catch (IOException e) {
			throw new IllegalStateException("unable to get canonical path for [" + f1 + "]");
		}
		
		String s2 = null;
		try {
			s2 = f2.getCanonicalPath();
		} catch (IOException e) {
			throw new IllegalStateException("unable to get canonical path for [" + f2 + "]");
		}
		
		if(s1.equals(s2)){
			return "";
		}
		
		File sp = getSharedPath(new File[]{f1, f2});
		if(sp == null){
			return null;
		}
		
		//f1 -> shared parent -> f2
		File _f = new File(f1.getAbsolutePath());
		StringBuffer sb = new StringBuffer();
		try {
			while(_f != null && !_f.getCanonicalPath().equals(sp.getCanonicalPath())){
				sb.append(File.separator + "..");
				_f = _f.getParentFile();
			}
		} catch (IOException e) {
			throw new IllegalStateException("IOException while performing [f1 = " + f1.getAbsolutePath() + "] -> [shared parent = " + sp.getAbsolutePath() + "] : " + e);
		}
		
		//shared parent -> f2
		try {
			s2 = s2.replace(sp.getCanonicalPath(), "");
		} catch (IOException e) {
			throw new IllegalStateException("IOException while performing [shared parent = " + sp.getAbsolutePath() + "] -> [f2 = " + f2.getAbsolutePath() + "] : " + e);
		}
		
		String sp1 = sb.toString();
		if(!sp1.endsWith(File.separator)){
			sp1 += File.separator;
		}
		
		if(s2.startsWith(File.separator)){
			s2 = s2.substring(s2.indexOf(File.separator));
		}
		
		return sp1 + s2;
	}
	
	public static void search(File f , FileFilter filter , List<File> l){
		if(f.isFile() && filter.accept(f)){
			l.add(f);
		}else if(f.isDirectory()){
			File[] files = f.listFiles();
			if(files != null && files.length > 0){
				for (int i = 0; i < files.length; i++) {
					search(files[i], filter, l);
				}
			}
		}
	}
	
	public static byte[] getBytes(File f){
		byte[] data = null;
		FileInputStream fis = null;
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			fis = new FileInputStream(f);
			byte[] b = new byte[8 * 1024];
			int len = -1;
			while((len = fis.read(b)) != -1){
				baos.write(b , 0 , len);
			}
			data = baos.toByteArray();
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			if(fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
		return data;
	}
	
	public static String getErrorMessage(Throwable t){
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		PrintStream ps = new PrintStream(baos);
		
		while(t != null){
			t.printStackTrace(ps);	
			t = t.getCause();
		}
		
		ps.close();
		byte[] data = baos.toByteArray();
		String errInfo = new String(data , 0 , data.length);
		
		return errInfo;
	}
}
